namespace devtm.Aop.Emit
{
    using System;
    using Mono.Cecil.Cil;
    using Mono.Cecil;


    [Serializable]
    public class EmitAssignStatement : EmitStatement
    {


        public EmitAssignStatement()
        {
        }

        public EmitAssignStatement(EmitExpression left, EmitExpression right)
        {
            this.Left = left;
            this.Right = right;
        }

        public EmitExpression Left { get; set; }
        public EmitExpression Right { get; set; }


        public override void Compile(CilWorker gen)
        {

           

            if (Left is EmitArrayIndexerExpression)
            {

                Right.UserData.Add("way", "get");
                Left.Compile(gen);
                Right.UserData.Remove("way");
                Right.Compile(gen);

                TypeDefinition r1 = GetTypeFromExpression(gen, Right);
                TypeDefinition l1 = GetTypeFromExpression(gen, Left);

                Box(gen, l1, r1);               

            }
            else
            {
                Right.UserData.Add("way", "get");
                Right.Compile(gen);
                Right.UserData.Remove("way");


                Left.UserData.Add("way", "set");
                Left.Compile(gen);
                Left.UserData.Remove("way");  

            }

            
        }

        private void Box(CilWorker gen, TypeDefinition l1, TypeDefinition r1)
        {

            if (l1.FullName == r1.FullName)
                return;

            if (r1.Module.Assembly.Name.Name == "mscorlib")
                AnalyzeMscorlib(gen, r1);

            else
            {
                AnalyzeOtherlib(gen, r1);
            }

        }

        private TypeDefinition GetTypeFromExpression(CilWorker gen, EmitExpression Right)
        {

            TypeReference reference;

            switch (Right.GetType().Name)
            {

                case "EmitArgumentReferenceExpression":

                    EmitArgumentReferenceExpression r = Right as EmitArgumentReferenceExpression;
                    reference = gen.Body.Method.Parameters[r.Index].ParameterType;
                    break;

                case "EmitArrayCreateExpression":
                    EmitArrayCreateExpression t = Right as EmitArrayCreateExpression;
                    reference = t.CreateType.Type.Resolve();
                    break;

                case "EmitVariableReferenceExpression":
                    EmitVariableReferenceExpression v = Right as EmitVariableReferenceExpression;
                    reference = v.Variable.VariableType.Resolve();
                    break;

                case "EmitArrayIndexerExpression":
                    EmitArrayIndexerExpression a = Right as EmitArrayIndexerExpression;
                    reference = GetTypeFromExpression(gen, a.TargetObject);
                    break;

                case "EmitMethodInvokeExpression":

                    return null;
                default:
                    throw new NotImplementedException();
            }


            var ret = gen.Body.Method.Module.Import(reference).Resolve();

            return ret;

        }



        private static void AnalyzeOtherlib(CilWorker gen, TypeDefinition r)
        {

            if (r.IsEnum)
            {

                gen.Insert(OpCodes.Box, gen.Body.Method.Module.Import(typeof(Int32)));

            }
            else
            {
                gen.Insert(OpCodes.Box, gen.Body.Method.Module.Import(r));
            }
           

            gen.Insert(OpCodes.Stelem_Ref);

        }


        private static void AnalyzeMscorlib(CilWorker gen, TypeDefinition r)
        {

            switch (r.Name)
            {
                case "Int32":
                    gen.Insert(OpCodes.Box, gen.Body.Method.Module.Import(typeof(Int32)));
                    break;

                case "String":

                    break;

                default:

                    throw new NotImplementedException();

            }

            gen.Insert(OpCodes.Stelem_Ref);

        }

    }
}
